package com.zhuoyue.biz.core.api.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.zhuoyue.biz.core.api.dao.*;
import com.zhuoyue.biz.core.api.domain.*;
import com.zhuoyue.biz.core.api.dto.rep.GroupDeviceRelationRepDTO;
import com.zhuoyue.biz.core.api.dto.rep.GroupQuickRepDTO;
import com.zhuoyue.biz.core.api.dto.rep.GroupRepDTO;
import com.zhuoyue.biz.core.api.dto.req.ControlReq;
import com.zhuoyue.biz.core.api.service.GatewayRedisService;
import com.zhuoyue.biz.core.api.service.GroupDeviceRelationService;
import com.zhuoyue.biz.core.command.impl.intelligence.GroupCmd;
import com.zhuoyue.common.constant.ControlType;

import com.zhuoyue.common.constant.RedisKey;
import com.zhuoyue.common.exception.ErrorType;
import com.zhuoyue.common.exception.ZyServerException;
import com.zhuoyue.common.utils.CmdUtils;
import com.zhuoyue.common.utils.Time;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.text.ParseException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhuoyue.biz.core.api.service.GroupService;
import org.springframework.transaction.annotation.Transactional;

/**
 * @author wjming
 * @ClassName: GroupServiceImpl
 * @Description:
 * @Date 2021-07-14 20:19
 */
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements GroupService {
    private final GatewayRedisService gatewayRedisService;
    private final GroupDeviceRelationMapper groupDeviceRelationMapper;
    private final GroupDeviceRelationService groupDeviceRelationService;
    private final GatewayDao gatewayDao;
    private final DeviceDao deviceDao;
    private final DeviceService deviceService;
    private final RedisTemplate redisTemplate;
    private final StringRedisTemplate stringRedisTemplate;
    private final GroupMapper groupMapper;
    private final GroupQuickMapper groupQuickMapper;
    private static final String ADD = "add";
    private static final String DELETE = "delete";
    private static final String CANCEL_DELAY_REQUEST = "CancelDelayRequest";
    private static final String LIGHT = "light";
    private static final int DEVICE_GROUP_COUNT = 7;

    @Override
    public void createGroupDeviceRelation(List<String> deviceId, Group group, String type) {
        String tp = "02";

        /**
         * 验证设备是否已经加入到8个群组中
         */
        if (ADD.equals(type)) {
            deviceId.stream().forEach(list -> {
                Integer integer = groupDeviceRelationMapper.selectCount(Wrappers.<GroupDeviceRelation>lambdaQuery()
                        .eq(GroupDeviceRelation::getDeviceId, list));
                if (integer > DEVICE_GROUP_COUNT) {
                    String name = deviceDao.selectById(list).getName();
                    throw new ZyServerException(ErrorType.INVALID_PARAMS.getCode(), name + "群组配置已满");
                }
            });
            tp = "01";
        }


        GatewayDO gatewayDO = gatewayDao.selectOne(Wrappers.<GatewayDO>lambdaQuery().eq(GatewayDO::getHomeId, group.getHomeId()));

        List<DeviceDO> deviceMaces = deviceService.listByIds(deviceId);
        StringBuilder stringBuilder = new StringBuilder();
        //持久化群组和设备之间的关系
        List<GroupDeviceRelation> groupDeviceRelations = new ArrayList<>(32);
        if (CollectionUtils.isEmpty(deviceMaces)) {
            throw new ZyServerException(ErrorType.result_not_exist);
        }
        for (DeviceDO device : deviceMaces) {
            stringBuilder.append(device.getZigbeeMac());
            GroupDeviceRelation groupDeviceRelation = new GroupDeviceRelation();
            groupDeviceRelation.setDeviceId(device.getId());
            groupDeviceRelation.setGroupId(group.getId());
            groupDeviceRelations.add(groupDeviceRelation);
        }
        String randomCode = RandomStringUtils.randomNumeric(4);
        String cmd = GroupCmd.getCommandSet(gatewayDO.getZigbeeMac(), stringBuilder.toString(), deviceMaces.size(), group.getCode(), tp, randomCode);
        String key = "";
        if (ADD.equals(type)) {
            key = RedisKey.groupAddMultipleDevices(gatewayDO.getZigbeeMac(), randomCode);
        }
        if (DELETE.equals(type)) {
            key = RedisKey.groupDeleteMultipleDevices(gatewayDO.getZigbeeMac(), randomCode);
        }
        stringRedisTemplate.opsForValue().set(key, JSONObject.toJSONString(groupDeviceRelations), 15, TimeUnit.SECONDS);

        gatewayRedisService.writeDataToGateway(gatewayDO.getZigbeeMac(), cmd);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteGroupAndGroupSet(Group group) {
        List<GroupDeviceRelation> list = groupDeviceRelationService.list(Wrappers.<GroupDeviceRelation>lambdaQuery()
                .eq(GroupDeviceRelation::getGroupId, group.getId()));
        if (CollectionUtils.isEmpty(list)) {
            groupMapper.deleteById(group.getId());
        } else {
            throw new ZyServerException(ErrorType.INVALID_PARAMS.getCode(), "请先删除群组中的设备后再尝试删除!");
        }
    }


    @Override
    public void sendAddCmd(String add, String addList) throws InterruptedException {
        Object cmdList = redisTemplate.opsForValue().get(add);
        if (cmdList == null) {
            throw new ZyServerException(ErrorType.result_not_exist.getCode(), "数据已失效，请重新选择设备");
        }
        List<String> cmds = (List<String>) cmdList;
        if (CollectionUtils.isEmpty(cmds)) {
            return;
        }
        String gatewayMac = StringUtils.split(add, "_")[0];
        String groupId = StringUtils.split(add, "_")[1];
        List<String> addListObj = (List<String>) redisTemplate.opsForValue().get(addList);
        //添加群组和设备之间的关系
        for (String str : addListObj) {
            groupDeviceRelationMapper.insert(GroupDeviceRelation.builder().deviceId(str).groupId(groupId).build());
        }

        for (String str : cmds) {
            //毫秒
            Thread.sleep(100);
            gatewayRedisService.writeDataToGateway(gatewayMac, str);
        }
        redisTemplate.delete(cmdList);
        redisTemplate.delete(addList);
    }

    @Override
    public void sendClearCmd(String clear, String clearList) throws InterruptedException {
        Object cmdClearList = redisTemplate.opsForValue().get(clear);
        if (cmdClearList == null) {
            throw new ZyServerException(ErrorType.result_not_exist.getCode(), "数据已失效，重新选择设备");
        }
        List<String> cmds = (List<String>) cmdClearList;
        if (CollectionUtils.isEmpty(cmds)) {
            return;
        }
        String gatewayMac = StringUtils.split(clear, "_")[0];
        String groupId = StringUtils.split(clear, "_")[1];
        List<String> addListObj = (List<String>) redisTemplate.opsForValue().get(clearList);
        //添加群组和设备之间的关系
        for (String str : addListObj) {
            groupDeviceRelationMapper.delete(Wrappers.<GroupDeviceRelation>lambdaQuery()
                    .eq(GroupDeviceRelation::getGroupId, groupId).eq(GroupDeviceRelation::getDeviceId, str));
        }
        for (String str : cmds) {
            //毫秒
            Thread.sleep(100);
            gatewayRedisService.writeDataToGateway(gatewayMac, str);
        }
        redisTemplate.delete(clear);
        redisTemplate.delete(clearList);
    }

    @Override
    public List<GroupRepDTO> selectByGroups(String gatewayMac) {//原参数为homeId，调整之后为gatewayMac
        /*List<Group> groups = groupMapper.selectList(Wrappers.<Group>lambdaQuery().eq(Group::getHomeId, homeId));*/
        List<Group> groups = groupMapper.selectList(Wrappers.<Group>lambdaQuery().eq(Group::getGatewayMac, gatewayMac));
        if (CollectionUtils.isEmpty(groups)) {
            return Collections.emptyList();
        }
        List<GroupRepDTO> dtos = new ArrayList<>();
        for (Group group : groups) {
            GroupRepDTO groupRepDTO = new GroupRepDTO();
            List<GroupDeviceRelation> groupDeviceRelations = groupDeviceRelationMapper.selectList(Wrappers.<GroupDeviceRelation>lambdaQuery()
                    .eq(GroupDeviceRelation::getGroupId, group.getId()));

            List<GroupQuick> groupQuicks = groupQuickMapper.selectList(Wrappers.<GroupQuick>lambdaQuery()
                    .eq(GroupQuick::getGroupId, group.getId()));

            List<GroupQuickRepDTO> repDtos = new ArrayList<>();
            groupQuicks.stream().forEach(list -> {
                GroupQuickRepDTO groupQuickRepDTO = new GroupQuickRepDTO();
                BeanUtils.copyProperties(list, groupQuickRepDTO);
                repDtos.add(groupQuickRepDTO);
            });

            List<GroupDeviceRelationRepDTO> relationRepDtos = new ArrayList<>();
            groupDeviceRelations.stream().forEach(list -> {
                GroupDeviceRelationRepDTO groupDeviceRelationRepDTO = new GroupDeviceRelationRepDTO();
                BeanUtils.copyProperties(list, groupDeviceRelationRepDTO);
                relationRepDtos.add(groupDeviceRelationRepDTO);
                Integer integer = groupDeviceRelationMapper.selectCount(Wrappers.<GroupDeviceRelation>lambdaQuery()
                        .eq(GroupDeviceRelation::getDeviceId, list.getDeviceId()));
                groupDeviceRelationRepDTO.setDeviceAddGroupCount(integer);
            });
            BeanUtils.copyProperties(group, groupRepDTO);
            dtos.add(groupRepDTO);
            groupRepDTO.setDeviceCount(groupDeviceRelations.size());
            groupRepDTO.setQuickCount(groupQuicks.size());
            groupRepDTO.setGroupQuickRelation(repDtos);
            groupRepDTO.setGroupDeviceRelation(relationRepDtos);
        }
        return dtos;
    }

    @Override
    public GroupRepDTO selectGroupsDetailsById(String groupId) {
        Group groups = groupMapper.selectById(groupId);
        if (groups == null) {
            throw new ZyServerException(ErrorType.result_not_exist.getCode(), "没有群组信息");
        }
        List<GroupDeviceRelation> groupDeviceRelations = groupDeviceRelationMapper.selectList(Wrappers.<GroupDeviceRelation>lambdaQuery()
                .eq(GroupDeviceRelation::getGroupId, groupId));

        List<GroupQuick> groupQuickList = groupQuickMapper.selectList(Wrappers.<GroupQuick>lambdaQuery()
                .eq(GroupQuick::getGroupId, groupId));

        GroupRepDTO groupRepDTO = new GroupRepDTO();

        List<GroupQuickRepDTO> groupQuickRepDtos = new ArrayList<>();
        groupQuickList.stream().forEach(list -> {
            GroupQuickRepDTO groupQuickRepDTO = new GroupQuickRepDTO();
            BeanUtils.copyProperties(list, groupQuickRepDTO);
            groupQuickRepDtos.add(groupQuickRepDTO);
        });

        List<GroupDeviceRelationRepDTO> groupDeviceRelationRepDtos = new ArrayList<>();

        groupDeviceRelations.stream().forEach(list -> {
            GroupDeviceRelationRepDTO groupDeviceRelationRepDTO = new GroupDeviceRelationRepDTO();
            BeanUtils.copyProperties(list, groupDeviceRelationRepDTO);
            groupDeviceRelationRepDtos.add(groupDeviceRelationRepDTO);
            Integer integer = groupDeviceRelationMapper.selectCount(Wrappers.<GroupDeviceRelation>lambdaQuery()
                    .eq(GroupDeviceRelation::getDeviceId, list.getDeviceId()));
            groupDeviceRelationRepDTO.setDeviceAddGroupCount(integer);
        });

        BeanUtils.copyProperties(groups, groupRepDTO);
        groupRepDTO.setQuickCount(groupQuickList.size());
        groupRepDTO.setDeviceCount(groupDeviceRelations.size());
        groupRepDTO.setGroupDeviceRelation(groupDeviceRelationRepDtos);
        groupRepDTO.setGroupQuickRelation(groupQuickRepDtos);
        return groupRepDTO;
    }

    @Override
    public void deviceControl(Group group, String mode, Integer delayTime, Integer duration, Integer brightness, Integer colorTemperature, Integer modeIndex) throws ParseException {
        Boolean isTrue = true;
        GatewayDO gatewayDO = gatewayDao.selectOne(Wrappers.<GatewayDO>lambdaQuery()
                .eq(GatewayDO::getHomeId, group.getHomeId()));
        Map<String, Object> sswMap = new HashMap<>(5);
        Map<String, Object> action = new HashMap<>(5);
        if (duration != null) {
            action.put("duration", duration + "");
        }
        if (delayTime != null) {
            action.put("delayTime", delayTime + "");
        }
        if (colorTemperature != null) {
            action.put("setColorTemperature", colorTemperature + "");
        }
        if (brightness != null) {
            action.put("setBrightness", brightness + "");
        }
        if (modeIndex != null) {
            action.put("modeIndex", modeIndex);
        }
        sswMap.put("actions", JSONObject.toJSONString(action));
        sswMap.put("controlType", mode);
        String cmdControl = "";
        if (CANCEL_DELAY_REQUEST.equals(mode)) {
            String cancelCmd = cancelDelay(gatewayDO.getZigbeeMac(), group.getCode());
            gatewayRedisService.writeDataToGateway(gatewayDO.getZigbeeMac(), cancelCmd);
            redisTemplate.delete(group.getId() + "_delayTime");
            System.out.println("取消延时设置成功: " + cancelCmd);
            isTrue = false;
        } else {
            cmdControl = GroupCmd.commandDeal(group.getType(), sswMap, gatewayDO.getZigbeeMac(), group.getCode());
        }


        if (LIGHT.equals(group.getType())) {
            if (ControlType.TURN_OFF_REQUEST.equals(mode)) {
                if (delayTime == null || delayTime == 0) {
                    redisTemplate.delete(group.getId() + "_delayTime");
                    System.out.println("延时取消成功");
                }
                if (delayTime != null && delayTime > 0) {
                    Long aLong = Time.toLongMilli(LocalDateTime.now()) + delayTime * 1000;
                    redisTemplate.opsForValue().set(group.getId() + "_delayTime", aLong, delayTime, TimeUnit.SECONDS);
                    //拼接命令
                    String data = StringUtils.substring(cmdControl, 4, cmdControl.length() - 4);
                    String time = StringUtils.substring(data, data.length() - 20, data.length() - 16);
                    //获取时间并且替换一下
                    StringBuilder daStr = new StringBuilder(data);
                    daStr.replace(data.length() - 20, data.length() - 16, "0000");
                    String delayCmd = groupDelayTimeCmd(gatewayDO.getZigbeeMac(), group.getCode(), time, daStr.toString());
                    gatewayRedisService.writeDataToGateway(gatewayDO.getZigbeeMac(), delayCmd);
                    isTrue = false;
                    System.out.println("延时设置成功: " + delayCmd);
                }
            }
        }
        System.out.println("群组设备命令控制" + cmdControl);
        if (isTrue) {
            gatewayRedisService.writeDataToGateway(gatewayDO.getZigbeeMac(), cmdControl);
        }

    }

    @Override
    public void deviceControl(Group group, ControlReq controlReq) throws ParseException {
        Boolean isTrue = true;
        GatewayDO gatewayDO = gatewayDao.selectOne(Wrappers.<GatewayDO>lambdaQuery()
                .eq(GatewayDO::getHomeId, group.getHomeId()));
        String cmdControl;

        cmdControl = GroupCmd.commandDeal(group, controlReq);
        System.out.println("群组设备命令控制" + cmdControl);
        if (isTrue) {
            gatewayRedisService.writeDataToGateway(gatewayDO.getZigbeeMac(), cmdControl);
        }

    }


    private String cancelDelay(String gatewayMac, String code) {
        String randomCode = RandomStringUtils.randomNumeric(4);
        String data1 = "53" + "00" + gatewayMac + "02" + randomCode + code;
        String cmd2 = CmdUtils.getDateLength(data1);

        String cmd13 = CmdUtils.getCmdBodySum(data1);
        return "2A" + cmd2 + data1 + cmd13 + "23";
    }

    private String groupDelayTimeCmd(String gatewayMac, String code, String time, String data) {
        String randomCode = RandomStringUtils.randomNumeric(4);
        String data1 = "53" + "00" + gatewayMac + "01" + randomCode + code + code + "00" + time + data;
        String cmd2 = CmdUtils.getDateLength(data1);

        String cmd13 = CmdUtils.getCmdBodySum(data1);
        return "2A" + cmd2 + data1 + cmd13 + "23";
    }

    @Override
    public void deleteGroupAndDeviceRelation(GroupDeviceRelation groupDeviceRelation) {
        Group group = groupMapper.selectById(groupDeviceRelation.getGroupId());
        //先把删除信息放到redis然后再发送命令删除
        String randomCode = RandomStringUtils.randomNumeric(4);
        DeviceDO deviceDO = deviceDao.selectAllDeviceById(groupDeviceRelation.getDeviceId());
        String cmd = GroupCmd.oneDeviceDeal(deviceDO.getGatewayMac(), deviceDO.getZigbeeMac(), group.getCode() + "00", "04", randomCode);
        String key = RedisKey.deleteOneDeviceOnGroup(deviceDO.getGatewayMac(), randomCode);
        stringRedisTemplate.opsForValue().set(key, groupDeviceRelation.getId(), 4, TimeUnit.SECONDS);
        gatewayRedisService.writeDataToGateway(deviceDO.getGatewayMac(), cmd);
        System.out.println("cmd = " + cmd);
    }


}

